package gang.org.springframework.boot;

import gang.org.springframework.boot.context.event.GangEventPublishingRunListener;
import gang.org.springframework.boot.loader.GangBeanDefinitionLoader;
import gang.org.springframework.framework.bean.GangBeanUtils;
import gang.org.springframework.framework.context.GangApplicationContext;
import gang.org.springframework.framework.context.GangApplicationContextInitializer;
import gang.org.springframework.framework.context.GangApplicationListener;
import gang.org.springframework.framework.context.GangConfigurableApplicationContext;
import gang.org.springframework.framework.io.GangResourceLoader;
import gang.org.springframework.framework.support.GangBeanDefinitionRegistry;
import gang.org.springframework.framework.support.GangSpringFactoriesLoader;
import gang.org.springframework.framework.util.GangClassUtils;

import java.lang.reflect.Constructor;
import java.util.*;

/**
 * @author gang.chen
 * @description 启动类
 * @Date 2021/6/10 8:50
 */
public class GangSpringApplication {

    private GangResourceLoader resourceLoader;

    private Set<Class<?>> primarySource;

    private GangWebApplicationType webApplicationType;

    private List<GangApplicationListener> listeners;

    private List<GangApplicationContextInitializer<?>> initializers;


    //#############################################################
    //GangApplicationContextFactory
    //函数式接口，获取函数式接口本身
    //#############################################################
    private GangApplicationContextFactory applicationContextFactory = GangApplicationContextFactory.DEFAULT;

    /**
     * @param primarySource 主起动类 value ->SpringbootManualApplication.class
     * */
    public GangSpringApplication(Class<?>... primarySource){
        this(null,primarySource);
    }

    //TODO
    public GangSpringApplication(GangResourceLoader resourceLoader, Class<?>... primarySource){
        this.primarySource = new LinkedHashSet<>(Arrays.asList(primarySource));

        /**
         * ApplicationContextInitializer接口类业务处理
         * 获取所有实现ApplicationContextInitializer接口类
         * */
        //setInitializers($)

        /**
         * ApplicationListener接口类业务处理
         * 获取所有实现ApplicationListener接口类
         * */
        setListeners();
    }


    /**
     * @param primaryClass 入参 SpringbootManualApplication.class
     * */
    public static GangConfigurableApplicationContext run (Class<?> primaryClass, String ... args){
        return new GangSpringApplication(primaryClass).run(args);
    }



    public GangConfigurableApplicationContext  run(String[] args){

        System.out.println("GangSpringApplication-> starting ...");

        GangConfigurableApplicationContext context = null;

        GangSpringApplicationRunListeners listeners = getRunListeners(args);

        listeners.starting(null,null);

        try
        {
            GangApplicationArguments applicationArguments = new GangDefaultApplicationArguments(args);

            context = createApplicationContext();

            //#############################################################
            //prepareContext
            //把启动类SpringBootManualApplication.class加载到 BeanDefinition
            //#############################################################
            prepareContext(null,context,null,listeners,null ,null);

            //#############################################################
            //refreshContext
            //Spring IOC 入口中
            //#############################################################
            refreshContext(context);

            afterRefresh(context,applicationArguments);

            listeners.started(context);

            listeners.running(context);
        }
        catch (Exception e){
            e.printStackTrace();
        }

        System.out.println("GangSpringApplication-> end ...");
        return context;
    }

    protected GangConfigurableApplicationContext createApplicationContext(){
        return this.applicationContextFactory.create();
    }

    private void refreshContext(GangConfigurableApplicationContext context){
        refresh(context);
    }

    private void refresh(GangConfigurableApplicationContext context){
        context.refresh();
    }

    private void afterRefresh(GangConfigurableApplicationContext context, GangApplicationArguments args){

    }

    /**
     * TODO
     * */
    private GangSpringApplicationRunListeners getRunListeners(String[] args){
        Class<?>[] types = new Class<?>[]{GangSpringApplication.class, String[].class};
        /**
         * 方法：getSpringFactoriesInstances（p1,p2,p3,p4）
         * 参数p3其传入的值为GangSpringApplication类的实例
         * */
        Collection<GangSpringApplicationRunListener> springApplicationRunListeners = getSpringFactoriesInstances(GangSpringApplicationRunListener.class, types,this, args);
        return new GangSpringApplicationRunListeners(null, springApplicationRunListeners, null);
    }

    public void setInitializers(Collection<? extends GangApplicationContextInitializer<?>> initializers){
        this.initializers = new ArrayList(initializers);
    }


    /**
     * TODO
     * @param context {@link gang.org.springframework.boot.context.GangAnnotationConfigServletWebServerApplicationContext}
     * */
    private void prepareContext(Object bootstrapContext,
                                GangConfigurableApplicationContext context,
                                Object environment,
                                GangSpringApplicationRunListeners listeners,
                                Object applicationArguments,
                                Object printedBanner)
    {
        /**
         * ApplicationContextInitializer接口类业务处理
         * 所有实现ApplicationContextInitializer接口的类，调用接口initialize方法，实现业务处理
         * */
        //applyInitializers($)

        Set<Object> sources = getAllSources();
        load(context,sources.toArray(new Object[0]));

        listeners.contextLoaded(context);
    }

    public Set<Object> getAllSources(){
        Set<Object> allSource = new LinkedHashSet<>();
        allSource.addAll(this.primarySource);
        return allSource;
    }

    //TODO
    private GangBeanDefinitionRegistry getBeanDefinitionRegistry(GangApplicationContext context){
        return (GangBeanDefinitionRegistry) context;
    }

    /**
     * TODO
     * @param sources {@link com.gang.mars.boot.SpringbootManualApplication}
     * @param context {@link gang.org.springframework.boot.context.GangAnnotationConfigServletWebServerApplicationContext}
     * */
    protected void load(GangApplicationContext context, Object[] sources)
    {
        GangBeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context),sources);
        loader.load();

    }

    protected GangBeanDefinitionLoader createBeanDefinitionLoader(GangBeanDefinitionRegistry registry, Object[] sources){
        return new GangBeanDefinitionLoader(registry,sources);
    }

    /**
     * TODO
     * 业务意义需要追加
     * */
    public ClassLoader getClassLoader(){
        return GangClassUtils.getDefaultClassLoader();
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type){
        return getSpringFactoriesInstances(type, new Class<?>[]{});
    }

    /**
     * TODO
     * 业务意义
     * 从spring.factories资源配置类中，获取接口类实现类的工厂对象
     * 场景一 接口类 SpringApplicationRunListener
     * @param type spring.factories工厂资源文件中接口类名
     * */
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args){
        List<T> instances = null;

        ClassLoader classLoader = getClassLoader();
        /**
         * 获取资源配置类中，指定接口，所有对该接口的实现工厂类名，并去重处理
         * */
        List<String> classNames = GangSpringFactoriesLoader.loadFactoryNames(type, classLoader);
        Set<String> instancesStr = new LinkedHashSet<>(classNames);

        instances = createSpringFactoriesInstances(type,parameterTypes,classLoader,args,instancesStr);

        return instances;
    }

    /**
     * 业务意义
     * @param parameterTypes class类实例对象，构造方法所需类对象,参数类{@link GangEventPublishingRunListener}，该类的构造方法是
     *                       new GangEventPublishingRunListener(GangSpringApplication application, String[] args)
     *
     * @param args 对应构造方法中，对象实例的值
     *
     * */
    @lombok.SneakyThrows
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
                                                       Class<?>[] parameterTypes,
                                                       ClassLoader classLoader,
                                                       Object[] args, Set<String> names) {

        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            Class<?> instanceClass = GangClassUtils.forName(name, classLoader);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            T instance = (T)GangBeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        return instances;
    }

    /**
     * 业务意义
     * 从工厂资源中，解析所有实现接口GangApplicationListener类，并存于集合中
     * */
    public void setListeners(){
        Collection<GangApplicationListener> springFactoriesInstances = getSpringFactoriesInstances(GangApplicationListener.class);
        this.listeners = new ArrayList<>(springFactoriesInstances);
    }

    //TODO
    private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> element){
        return new LinkedHashSet<>(element);
    }

    public Set<GangApplicationListener> getListeners(){
        return asUnmodifiableOrderedSet(this.listeners);
    }


}
